#include <iostream>
#include <fstream>
#include <boost/algorithm/string/split.hpp>
#include <boost/algorithm/string/classification.hpp>
#include <boost/bind.hpp>
#include "helper/helper.h"
#include "FileAccess.h"
#include "MemoryDataStore.h"

MemoryDataStore::MemoryDataStore(const std::string& dataFile, DataFormat format, int hint)
  : m_DataFile(dataFile)
{
  switch (format) {
  case LIBSVM:
    readLibSVM(hint);
    break;
  default:
    std::cerr << "Unknown data format" << std::endl;
    exit(1);
  }
}


MemoryDataStore::~MemoryDataStore(void)
{
}

void MemoryDataStore::readLibSVM(int hint)
{
  int nExample, nDim = hint;
  scanLibSVM(nExample, nDim, m_IsLabeled);

  FileAccessRead(input, m_DataFile, inputBuffer);
  for (int i = 0; i < nExample; i++) {
    arma::sp_mat x(nDim+1, 1);
    double y;
    while (!readLibSVMRow(input, x, y));
    m_Examples.push_back(Example(x, y));		

    //std::cerr << y;
    //for (int i = 0; i <= nDim; i++) if (x(i,0) != 0) std::cerr << " " << i << ":" << x(i,0);
    //std::cerr << std::endl;
  }
}

void MemoryDataStore::scanLibSVM(int& nExample, int& nDim, bool& isLabeled)
{
  FileAccessRead(input, m_DataFile, inputBuffer);
  nExample = 0;
  isLabeled = false;
  int nLine = 0;
  while (!input.eof()) {
    nLine ++; //std::cerr << nLine << " ";
    std::string line;
    std::getline(input, line);
    std::vector<std::string> tokens;
    BOOST_split(line, tokens, "\t :\r\n");
    //boost::split(tokens, line, boost::algorithm::is_any_of("\t :\r\n"), boost::algorithm::token_compress_on);
    //tokens.erase(std::remove_if(tokens.begin(), tokens.end(), boost::bind(&std::string::empty, _1)), tokens.end());

    //for (auto& tk : tokens) std::cerr << tk << ": ";
    //std::cerr << std::endl;

    if (tokens.size() <= 1) continue;
    if (tokens.size() % 2 == 1) isLabeled = true;
    else if (isLabeled) { 
      std::cerr << "error in data file " << m_DataFile << " line=" << nLine << std::endl;
      exit(1);
    }

    nExample++;
    int start = 0;
    if (isLabeled) {
      start = 1;
    }

    for (unsigned int i = start; i < tokens.size(); i+=2) {
      int feature = atoi(tokens[i].c_str());
      if (feature > nDim) nDim = feature;
    }
  }
  //std::cerr << "n=" << nExample << " d=" << nDim << " label=" << isLabeled << std::endl;
}

bool MemoryDataStore::readLibSVMRow(std::ifstream& input, arma::sp_mat& x, double& y) const
{
  std::string line;
  std::getline(input, line);
  std::vector<std::string> tokens;
  BOOST_split(line, tokens, "\t :\r\n");

  //boost::split(tokens, line, boost::algorithm::is_any_of("\t :\r\n"), boost::algorithm::token_compress_on);
  //tokens.erase(std::remove_if(tokens.begin(), tokens.end(), boost::bind(&std::string::empty, _1)), tokens.end());
  if (tokens.size() <= 1) return false;

  int start = 0;
  if (m_IsLabeled) {
    start = 1;
    y = atof(tokens[0].c_str());
    //std::cerr << y;
  }

  for (unsigned int i = start; i < tokens.size(); i+=2) {
    int feature = atoi(tokens[i].c_str());
    double value = atof(tokens[i+1].c_str());
    //std::cerr << " " << feature << ":" << value;
    x(feature, 0) = value; // libsvm start from 1
  }
  //std::cerr << std::endl;
  //std::cerr << "y=" << y << std::endl << "x=" << x;
  return true;
}
